<!DOCTYPE html>
<html>

<head>
  <meta charset='utf-8'>
  <title>鼠标控制物体旋转</title>
  <link rel="shortcut icon" href="../icon/sparrow.png" />
  <link rel="bookmark" href="../icon/sparrow.png" type="image/x-icon" />
  <link rel="stylesheet" href="../style/common.css" />
  <!-- 对于制作原型或学习，你可以这样使用最新版本： -->
  <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
  <script src="../lib/webgl-debug.js"></script>
  <script src="../lib/webgl-utils.js"></script>
  <script src="../lib/cuon-matrix.js"></script>
  <script src="../lib/cuon-utils.js"></script>
  <script src="../lib/allx-utils.js"></script>
</head>

<body>
  <div id='gl32'>
    <h2>gl32 多个简单模型组成的复杂模型</h2>
    <canvas id="c32" width="400" height="400">
      您的浏览器不支持canvas
    </canvas>
    <script type="text/javascript">
      // 顶点着色器程序2
      let VSHADER_SOURCE32 = `
        attribute vec4 a_Position; // 顶点坐标
        attribute vec4 a_Color; // 顶点颜色
        attribute vec4 a_Normal; // 法向量

        uniform mat4 u_MvpMatrix; // 模型视图投影矩阵
        uniform mat4 u_ModelMatrix; // 模型矩阵
        uniform mat4 u_NormalMatrix; // 用来变换法向量的矩阵
        // uniform vec3 u_LightDirection; // 光线归一化方向（平行光使用）

        varying vec4 v_Color; // 向片元着色器传递颜色
        varying vec3 v_Normal;
        varying vec3 v_Position;

        void main(){
          gl_Position = u_MvpMatrix * a_Position;

          v_Position = vec3(u_ModelMatrix * a_Position); // 计算顶点的世界坐标
          // v_Normal = normalize(vec3(a_Normal)); // 归一化法向量
          v_Normal = normalize(vec3(u_NormalMatrix * a_Normal)); // 计算变换后的法向量并归一化法向量
          v_Color = a_Color;
        }`;

      // 片元着色器程序2
      let FSHADER_SOURCE32 = `
        precision mediump float; // 声明片元着色器float的精度（必须指定）

        uniform vec3 u_LightColor; // 光线颜色
        uniform vec3 u_LightPosition; // 光源位置（世界坐标系）
        uniform vec3 u_AmbientLight; // 环境光颜色

        varying vec4 v_Color; // 接收顶点着色器传递的颜色
        varying vec3 v_Normal; 
        varying vec3 v_Position;

        void main(){
          vec3 normal = normalize(v_Normal); // 对插值后的法向量进行归一化（内插后长度不一定为1.0）
          vec3 lightDirection = normalize(u_LightPosition - v_Position); // 计算光线方向并归一化
          float nDotL = max(dot(lightDirection, normal), 0.0); // 计算光线方向和法向量的点积

          vec3 diffuse = u_LightColor * vec3(v_Color) * nDotL; // 计算漫反射光的颜色
          vec3 ambient = u_AmbientLight * v_Color.rgb; // 计算环境反射光颜色

          gl_FragColor = vec4(diffuse + ambient, v_Color.a); // 漫反射光 + 环境反射光
          // gl_FragColor = vec4(1.0, 0.0, 0.0, 1.0); // 漫反射光 + 环境反射光
        }`;

      // 获取canvas，获取webGL绘图上下文
      const gl32 = document.querySelector("#c32").getContext("webgl");
      if (!gl32) console.log(Error("无法获得webgl的绘图上下文"));

      // 初始化着色器
      if (!initShaders(gl32, VSHADER_SOURCE32, FSHADER_SOURCE32)) console.log(Error("无法初始化着色器"));

      // 设置顶点信息（传入attribute变量）
      let n32 = initVertexBuffers32(gl32);
      if (n32 < 0) console.log(Error("无法设置顶点信息（传入attribute变量）"));

      gl32.enable(gl32.DEPTH_TEST); // 开启深度检测（隐藏面消除）
      gl32.clearColor(0.0, 0.0, 0.0, 1.0); // 设置背景色

      // 获取 uniform 变量位置
      let u_mvpMatrix32 = gl32.getUniformLocation(gl32.program, "u_MvpMatrix");
      let u_modelMatrix32 = gl32.getUniformLocation(gl32.program, "u_ModelMatrix");
      let u_normalMatrix32 = gl32.getUniformLocation(gl32.program, "u_NormalMatrix");
      let u_lightColor32 = gl32.getUniformLocation(gl32.program, "u_LightColor");
      let u_ambientLight32 = gl32.getUniformLocation(gl32.program, "u_AmbientLight");
      let u_lightPosition32 = gl32.getUniformLocation(gl32.program, "u_LightPosition");

      let ANGLE_STEP32 = 3.0; // 每次按键转动的角度
      let g_arm1Angle32 = 90.0; // arm1当前的角度
      let g_joint1Angle32 = 0.0; // joint1的当前角度，即arm2的角度 

      let g_modelMatrix32 = new Matrix4(); // 全局 模型矩阵
      let g_mvpMatrix32 = new Matrix4(); // 全局 模型视图投影矩阵
      let g_normalMatrix32 = new Matrix4(); // 全局 魔法矩阵

      gl32.uniform3f(u_lightColor32, 1.0, 1.0, 1.0); // 传入 白色光线
      gl32.uniform3f(u_ambientLight32, 0.2, 0.2, 0.2); // 传入 环境光颜色 更真实
      gl32.uniform3f(u_lightPosition32, 5.0, 5.0, 10.0); // 传入 点光源位置（世界坐标）

      let viewProjMatrix32 = new Matrix4(); // 创建视图投影矩阵
      viewProjMatrix32.setPerspective(50.0, 1, 1.0, 100.0); // 垂直视角、近裁剪面宽高比、近裁剪面位置、远裁剪面位置
      viewProjMatrix32.lookAt(20.0, 10.0, 30.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0); // 视点、视线（被观察对象位置）、上方向

      // 绘制机器臂
      draw32(gl32, n32, viewProjMatrix32, u_mvpMatrix32, u_modelMatrix32, u_normalMatrix32);

      // 注册键盘事件
      document.addEventListener("keydown", (event) => {
        // console.log(event.which);
        handleKeyDown32(event, gl32, n32, viewProjMatrix32, u_mvpMatrix32, u_modelMatrix32, u_normalMatrix32);
      })

      function handleKeyDown32(event, gl, n, viewProjMatrix, u_mvpMatrix, u_modelMatrix, u_normalMatrix) {
        // console.log(event.which);
        switch (event.which) {
          case 87: // 上w，joint1绕Z轴正向转动
            if (g_joint1Angle32 < 135.0) g_joint1Angle32 += ANGLE_STEP32;
            break;
          case 83: // 下s，joint1绕Z轴负向转动
            if (g_joint1Angle32 > -135.0) g_joint1Angle32 -= ANGLE_STEP32;
            break;
          case 68: // 右d，joint1绕Y轴正向转动
            g_arm1Angle32 = (g_arm1Angle32 + ANGLE_STEP32) % 360;
            break;
          case 65: // 左a，joint1绕Y轴负向转动
            g_arm1Angle32 = (g_arm1Angle32 - ANGLE_STEP32) % 360;
            break;
          default:
            return;
        }
        draw32(gl32, n32, viewProjMatrix32, u_mvpMatrix32, u_modelMatrix, u_normalMatrix32);
      }

      function draw32(gl, n, viewProjMatrix, u_mvpMatrix, u_modelMatrix, u_normalMatrix) {
        gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT); // 清空颜色及深度缓冲区

        // 机器臂1
        let arm1Length = 10.0;
        g_modelMatrix32.setTranslate(0.0, -12.0, 0.0); // 因为是从原点开始绘制，所以需要往下移动
        g_modelMatrix32.rotate(g_arm1Angle32, 0, 1, 0); // 绕 Y 轴旋转
        drawBox32(gl, n, viewProjMatrix, u_mvpMatrix, u_modelMatrix, u_normalMatrix); // 绘制

        // 机器臂2
        g_modelMatrix32.translate(0.0, arm1Length, 0.0); // 移到 joint1 处
        g_modelMatrix32.rotate(g_joint1Angle32, 0, 0, 1); // 绕 Z 轴旋转
        g_modelMatrix32.scale(1.3, 1.0, 1.3); // 使立方体粗一点（机器臂2）
        drawBox32(gl, n, viewProjMatrix, u_mvpMatrix, u_modelMatrix, u_normalMatrix); // 绘制
      }

      function drawBox32(gl, n, viewProjMatrix, u_mvpMatrix, u_modelMatrix, u_normalMatrix) { // 绘制立方体
        gl.uniformMatrix4fv(u_modelMatrix, false, g_modelMatrix32.elements); // 模型矩阵

        g_mvpMatrix32.set(viewProjMatrix);
        g_mvpMatrix32.multiply(g_modelMatrix32);
        gl.uniformMatrix4fv(u_mvpMatrix, false, g_mvpMatrix32.elements); // 模型视图投影矩阵

        g_normalMatrix32.setInverseOf(g_modelMatrix32);
        g_normalMatrix32.transpose();
        gl.uniformMatrix4fv(u_normalMatrix, false, g_normalMatrix32.elements); // 魔法矩阵

        gl.drawElements(gl.TRIANGLES, n, gl.UNSIGNED_BYTE, 0); // 绘制
      }

      function initVertexBuffers32(gl) {
        var vertices = new Float32Array([
          1.5, 10.0, 1.5, -1.5, 10.0, 1.5, -1.5, 0.0, 1.5, 1.5, 0.0, 1.5, // v0-v1-v2-v3 front
          1.5, 10.0, 1.5, 1.5, 0.0, 1.5, 1.5, 0.0, -1.5, 1.5, 10.0, -1.5, // v0-v3-v4-v5 right
          1.5, 10.0, 1.5, 1.5, 10.0, -1.5, -1.5, 10.0, -1.5, -1.5, 10.0, 1.5, // v0-v5-v6-v1 up
          -1.5, 10.0, 1.5, -1.5, 10.0, -1.5, -1.5, 0.0, -1.5, -1.5, 0.0, 1.5, // v1-v6-v7-v2 left
          -1.5, 0.0, -1.5, 1.5, 0.0, -1.5, 1.5, 0.0, 1.5, -1.5, 0.0, 1.5, // v7-v4-v3-v2 down
          1.5, 0.0, -1.5, -1.5, 0.0, -1.5, -1.5, 10.0, -1.5, 1.5, 10.0, -1.5 // v4-v7-v6-v5 back
        ]);

        var colors = new Float32Array([ // Colors
          1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, // v0-v1-v2-v3 front
          1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, // v0-v3-v4-v5 right
          1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, // v0-v5-v6-v1 up
          1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, // v1-v6-v7-v2 left
          1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, // v7-v4-v3-v2 down
          1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0 // v4-v7-v6-v5 back
        ]);

        var normals = new Float32Array([ // Normal
          0.0, 0.0, 1.0, 0.0, 0.0, 1.0, 0.0, 0.0, 1.0, 0.0, 0.0, 1.0, // v0-v1-v2-v3 front
          1.0, 0.0, 0.0, 1.0, 0.0, 0.0, 1.0, 0.0, 0.0, 1.0, 0.0, 0.0, // v0-v3-v4-v5 right
          0.0, 1.0, 0.0, 0.0, 1.0, 0.0, 0.0, 1.0, 0.0, 0.0, 1.0, 0.0, // v0-v5-v6-v1 up
          -1.0, 0.0, 0.0, -1.0, 0.0, 0.0, -1.0, 0.0, 0.0, -1.0, 0.0, 0.0, // v1-v6-v7-v2 left
          0.0, -1.0, 0.0, 0.0, -1.0, 0.0, 0.0, -1.0, 0.0, 0.0, -1.0, 0.0, // v7-v4-v3-v2 down
          0.0, 0.0, -1.0, 0.0, 0.0, -1.0, 0.0, 0.0, -1.0, 0.0, 0.0, -1.0 // v4-v7-v6-v5 back
        ]);

        var indices = new Uint8Array([ // Indices of the vertices
          0, 1, 2, 0, 2, 3, // front
          4, 5, 6, 4, 6, 7, // right
          8, 9, 10, 8, 10, 11, // up
          12, 13, 14, 12, 14, 15, // left
          16, 17, 18, 16, 18, 19, // down
          20, 21, 22, 20, 22, 23 // back
        ]);

        const FSIZE = vertices.BYTES_PER_ELEMENT;

        if (!initArrayBuffer(gl, "a_Position", vertices, 3, gl.FLOAT)) return -1; // 写入ArrayBuffer缓冲区
        if (!initArrayBuffer(gl, "a_Color", colors, 3, gl.FLOAT)) return -1; // 写入ArrayBuffer缓冲区
        if (!initArrayBuffer(gl, "a_Normal", normals, 3, gl.FLOAT)) return -1; // 写ArrayBuffer入缓冲区

        let indexBuffer = gl.createBuffer();
        if (!indexBuffer) {
          console.log(Error('Failed to create index buffer'));
          return -1;
        }
        gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, indexBuffer);
        gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, indices, gl.STATIC_DRAW); // 写ELEMENT_ARRAY_BUFFER入缓冲区

        return indices.length;
      }
    </script>
  </div>
</body>

</html>